package com.market.manager.impl;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.market.dao.AccountRecordDao;
import com.market.dao.OrderDao;
import com.market.dao.OrderDetailDao;
import com.market.dao.UserDao;
import com.market.domain.AccountRecord;
import com.market.domain.AccountRecord.TYPE;
import com.market.domain.Order;
import com.market.domain.Order.STATUS;
import com.market.domain.OrderDetail;
import com.market.domain.User;
import com.market.domain.transients.OrderQuery;
import com.market.manager.OrderManager;
import com.market.manager.UserManager;

@Service("orderManager")
public class OrderManagerImpl extends GenericManagerImpl<OrderDao, Order, Long>
		implements OrderManager {

	@Autowired
	private UserManager userManager;

	@Autowired
	private OrderDetailDao orderDetailDao;

	@Autowired
	private AccountRecordDao accountRecordDao;

	@Autowired
	@Override
	public void setDao(OrderDao dao) {
		super.dao = dao;
	}

	public List<OrderDetail> fetchDetails(Order order) {
		return this.dao.findOrderDetails(order);
	}

	public List<Order> fetchOrders(OrderQuery orderQuery) {
		return this.dao.findOrders(orderQuery);
	}

	public Order fetchOne(Long id) {
		Order order = this.dao.findById(id);
		order.setOrderDetails(this.fetchDetails(order));
		order.setPromotions(this.dao.findPromotions(order));
		order.setIsAssociatedLoaded(Boolean.TRUE);
		return order;
	}

	public Order fetchOne(Order order) {
		if (order != null && order.getIsNew() == false) {
			return this.fetchOne(order.getId());
		} else {
			return order;
		}
	}

	/**
	 * Customer submit an unConfirmed(Received) order.
	 */
	public Order userStore(Order order) {
		User customer = order.getCustomer();
		customer = this.userManager.fetchOne(customer.getId()); // Reload
																// customer
																// info
																// from
																// database;
		Double balance = customer.getBalance();// Get current customer's
												// balance.

		if (balance > 0.01D) {
			Double orderedTotal = order.getOrderedTotalPlusTax();
			if (balance > orderedTotal) {
				order.setPaidByCreditAmount(orderedTotal);
				balance = balance - orderedTotal;
			} else {
				order.setPaidByCreditAmount(balance);
				balance = 0D;
			}
			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			record.setType(TYPE.ORDER_USED);
			record.setAmount(-order.getPaidByCreditAmount());
			record.setNewBalance(balance);
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.getAccountRecords().add(record);
			customer.setBalance(balance);
			this.userManager.store(customer);

		}
		return this.store(order);
	}

	public Order confirm(Order order) {
		if (order.getStatus() == STATUS.CONFIRMED) { // If status is Confirmed,
														// means the inventory
														// is already minus by
														// deliveredQuantity.
			List<OrderDetail> detailsFromDB = this.dao.findOrderDetails(order);
			for (OrderDetail detail : detailsFromDB) {
				this.orderDetailDao.removeAndUpdateInventory(detail);// Restore
																		// deliveredQuantity
																		// back
																		// to
																		// inventory.
			}
		}
		List<OrderDetail> details = order.getOrderDetails();
		for (OrderDetail detail : details) {
			// if (detail.getDeliveredPrice() == 0D) {
			// detail.setDeliveredPrice(detail.getOrderedPrice());
			// }
			// if (detail.getDeliveredQuantity() == 0) {
			// detail.setDeliveredQuantity(detail.getOrderedQuantity());
			// }

			this.orderDetailDao.confirm(detail);
			// this.orderDetailDao.saveAndUpdateInventory(detail);

		}
		if (order.getIsNew()) {
			order = super.store(order);
		}
		order.setStatus(STATUS.CONFIRMED);
		order.setConfirmedDate(new Date());
		order = super.store(order);
		return order;
	}

	public Order userConfirm(Order order) {
		User customer = order.getCustomer();
		customer = this.userManager.fetchOne(customer.getId()); // Reload
																// customer
																// info
																// from
																// database;
		Double balance = customer.getBalance();
		if (balance > 0.01D) {

			Double confirmedTotal = order.getConfirmedTotalPlusTax();
			if (balance > confirmedTotal) {
				order.setPaidByCreditAmount(confirmedTotal);
				balance = balance - confirmedTotal;
			} else {
				order.setPaidByCreditAmount(balance);
				balance = 0D;
			}

			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			record.setType(TYPE.ORDER_USED);
			record.setAmount(-order.getPaidByCreditAmount());
			record.setNewBalance(order.getCustomer().getBalance()
					- order.getPaidByCreditAmount());
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.setBalance(balance);
			customer.getAccountRecords().add(record);
			this.userManager.store(customer);
		}
		return this.confirm(order);
	}

	public Order unConfirm(Order order) {
		List<OrderDetail> details = order.getOrderDetails();
		for (OrderDetail detail : details) {
			this.orderDetailDao.unConfirm(detail);
		}
		order.setConfirmedDate(null);
		order.setStatus(STATUS.RECEIVED);
		order = super.store(order);
		return order;
	}

	public Order prepare(Order order) {
		for (OrderDetail detail : order.getOrderDetails()) {
			this.orderDetailDao.save(detail);
			// this.orderDetailDao.removeAndUpdateInventory(detail);
			// this.orderDetailDao.saveAndUpdateInventory(detail);
		}
		order.setPreparedDate(new Date());
		order.setStatus(STATUS.PREPARED);
		return super.store(order);
	}

	public Order unPrepare(Order order) {
		for (OrderDetail detail : order.getOrderDetails()) {
			detail.setDeliveredQuantity(0D);
			detail.setDeliveredPrice(0D);
			this.orderDetailDao.save(detail);
		}
		order.setPreparedDate(null);
		order.setStatus(STATUS.CONFIRMED);
		return super.store(order);
	}

	public Order deliver(Order order) {
		order.setDeliveredDate(new Date());
		order.setStatus(STATUS.DELIVERED);
		return super.store(order);
	}

	public Order unDeliver(Order order) {
		order.setDeliveredDate(null);
		order.setStatus(STATUS.PREPARED);
		return super.store(order);
	}

	public Order pickup(Order order) {
		order.setPickedUpDate(new Date());
		order.setStatus(STATUS.PICKED_UP);
		return super.store(order);
	}

	public Order unPickup(Order order) {
		order.setPickedUpDate(null);
		order.setStatus(STATUS.DELIVERED);
		return super.store(order);
	}

	public Order pay(Order order) {
		User customer = order.getCustomer();
		customer = this.userManager.fetchOne(customer.getId()); // Reload
																// customer info
																// from
																// database;
		Double balance = customer.getBalance();
		if (order.getPromotionRewardRate() > 0D) { // Deposit promotion credit
													// to user's account.
			Double reward = order.getDeliveredTotalAmount()
					* order.getPromotionRewardRate() / 100D;
			balance = balance + reward; // Deposit credit reward to user's
										// account, because payment.
			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			record.setType(TYPE.ORDER_PAID_PROMOTION_REWARD);
			record.setAmount(reward);
			record.setNewBalance(balance);
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.getAccountRecords().add(record);
		}

		Double due = order.getPaidAmount() + order.getDiscountAmount()
				- order.getDeliveredTotalCharge();

		if (due > 0.01 || due < -0.01) {
			balance = balance + due;
			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			if (due > 0D) {
				record.setType(TYPE.ORDER_PAID_DEPOSIT);
			} else {
				record.setType(TYPE.ORDER_PAID_WITHDRAL);
			}
			record.setAmount(due);
			record.setNewBalance(balance);
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.getAccountRecords().add(record);
		}
		customer.setBalance(balance);
		this.userManager.store(customer);
		order.setCustomer(customer);
		if (order.getPreparedDate() == null) {
			order.setPreparedDate(new Date());
		}
		if (order.getDeliveredDate() == null) {
			order.setDeliveredDate(new Date());
		}
		if (order.getPickedUpDate() == null) {
			order.setPickedUpDate(new Date());
		}
		order.setPaidDate(new Date());
		order.setStatus(STATUS.PAID);
		return super.store(order);
	}

	public Order unPay(Order order) {

		User customer = order.getCustomer();
		customer = this.userManager.fetchOne(customer.getId()); // Reload
																// customer info
																// from
																// database;
		Double balance = customer.getBalance();
		if (order.getPromotionRewardRate() > 0D) { // Deposit promotion credit
													// to user's account.
			Double reward = order.getDeliveredTotalAmount()
					* order.getPromotionRewardRate() / 100D;
			balance = balance - reward; // Take away credit reward, because of
										// refund.
			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			record.setType(TYPE.ORDER_UNPAID_PROMOTION_REWARD_CANCEL);
			record.setAmount(-reward);
			record.setNewBalance(balance);
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.getAccountRecords().add(record);
		}

		balance = balance
				+ (order.getDeliveredTotalPlusTax()
						- order.getPaidByCreditAmount() - order.getPaidAmount() - order
							.getDiscountAmount());
		customer.setBalance(balance);
		this.userManager.store(customer);
		order.setCustomer(customer);
		order.setPaidDate(null);
		order.setStatus(STATUS.PICKED_UP);
		order.setPaidAmount(0D);
		order.setDiscountAmount(0D);
		return super.store(order);
	}

	public Order cancel(Order order) {

		if (order.getPaidByCreditAmount() > 0D) {

			User customer = order.getCustomer();
			customer = this.userManager.fetchOne(customer.getId()); // Reload
																	// customer
																	// info
																	// from
																	// database;
			Double balance = customer.getBalance()
					+ order.getPaidByCreditAmount();
			AccountRecord record = new AccountRecord();
			record.setIsNew(true);
			record.setRecordDate(new Date());
			record.setType(TYPE.ORDER_CANCEL_RETURN);
			record.setAmount(order.getPaidByCreditAmount());
			record.setNewBalance(balance);
			record.setComments("order:" + order.getNumber() + " Date:"
					+ order.getOrderedDate());
			record = this.accountRecordDao.save(record);
			customer.setBalance(balance);
			customer.getAccountRecords().add(record);
			this.userManager.store(customer);
		}

		if (order.getStatus() == STATUS.CONFIRMED
				|| order.getStatus() == STATUS.PREPARED) {
			for (OrderDetail detail : order.getOrderDetails()) {
				this.orderDetailDao.unConfirm(detail);
			}
		}
		order.setPaidByCreditAmount(0D);
		order.setCanceledDate(new Date());
		order.setStatus(STATUS.CANCELED);
		return super.store(order);
	}

	public Order restore(Order order) {
		order.setPaidByCreditAmount(0D);
		order.setStatus(STATUS.RECEIVED);
		order.setConfirmedDate(null);
		order.setPreparedDate(null);
		order.setDeliveredDate(null);
		order.setPickedUpDate(null);
		order.setPaidDate(null);
		return super.store(order);
	}

}
